ci: post sticky PR comment with fallow audit findings#954
Conversation
7f10c63 to
3c5961a
Compare
af04a20 to
3d59ebb
Compare
This stack of pull requests is managed by Graphite. Learn more about stacking. |
vanceingalls
left a comment
There was a problem hiding this comment.
Workflow change is small, surgical, and well-commented. Three steps (run + capture, post sticky, re-fail) is the right decomposition and the sentinel-matching with fallow's <!-- fallow-id: fallow-results --> is the correct sticky pattern.
Audited: .github/workflows/ci.yml (entire file, end-to-end at 3d59ebb7); verified marocchino/sticky-pull-request-comment@52423e01 resolves to tag v2.9.1 and oven-sh/setup-bun@0c5077e5 resolves to v2; verified fallow ^2.75.0 is in root package.json so bunx fallow resolves from node_modules (PR-body claim holds).
Strengths
- The "Remove stale sticky comment (clean run)" step (
ci.yml:135-140) is the right call — without it, a PR that goes red then green would leave the findings comment around forever and reviewers would think the PR is still broken. Nice catch. set +e+ capturing$?toGITHUB_OUTPUT(ci.yml:121-126) is the cleanest way to keep the audit gating while still letting the sticky-post step run. Cleaner than relying oncontinue-on-errorat the step level.- The top-of-file comment on the
pull-requests: writewidening (ci.yml:5-9) is exactly the kind of forward-looking note that pays off later. It names the future split point (fork PRs → separate workflow) before someone else has to figure it out.
Findings
- important —
ci.yml:9(pull-requests: writeat the workflow level). This grants every job inci.ymlPR-write tokens — Build, Lint, Format, Typecheck, Test, both smoke jobs, CLI smoke, filesize, semantic-pr-title. Onlyfallowneeds it. The blast-radius concern is: any of those jobs runsbun installagainst user-controlledpackage.json(lifecycle scripts) orbun run buildagainst user-controlled source, all under a write-scoped token. The in-diff comment acknowledges this and defers; my push is that the right shape is available now — movepermissions:to the job level insidefallow:only and revert the top-level topull-requests: read. That keeps the audit-comment poster working without expanding any other job's authority. Job-levelpermissions:override the workflow-level block. One-line fix, no other tradeoffs. - nit —
ci.yml:127-133posts/tmp/fallow-comment.mdunconditionally on non-zero exit, but ifbunx fallowcrashes before producing meaningful markdown, the file may be empty or partial. Cheap guard:if [ ! -s /tmp/fallow-comment.md ]; then echo "fallow produced no output" > /tmp/fallow-comment.md; fiafter the audit. Avoids posting a blank sticky on infra failure. - nit —
ci.yml:124-125would benefit fromset -o pipefailin case fallow's output is ever piped (today it isn't; this is paranoia against future edits to the line). - nit — fork PRs: with the
pull_requesttrigger (not_target), GitHub gives forks a read-only token regardless of the workflowpermissions:block, so the sticky-post step will fail for fork contributors. Not a security issue (correct direction of failure), but worth a one-line graceful degradation —continue-on-error: trueon the two sticky-comment steps so a fork PR doesn't fail CI on a comment it can't post. The audit gate still fires via "Fail if audit found issues."
Notes
- Test-plan checkbox 3 ("Verify in CI: this PR should post a 'No findings' comment on itself") — given the "Remove stale sticky comment (clean run)" branch, a no-findings PR shouldn't post a comment; it should delete a (nonexistent) one. The verification on this PR is "no fallow comment appears." Worth nudging the description for clarity.
Verdict: APPROVE
Reasoning: The behavior change is correct and the design is sound. The workflow-level pull-requests: write is an important call-out the author already flagged for follow-up; scoping it to the fallow job in this PR would be one line and worth doing before merge, but it isn't a blocker given there's no fork trigger here.
Review by Vai
miguel-heygen
left a comment
There was a problem hiding this comment.
APPROVE — well-structured CI workflow.
Verified:
- Exit code capture pattern (
set +e→$?→GITHUB_OUTPUT) is correct marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728SHA matches v2.9.1- Sticky comment lifecycle is right: post on findings, delete on clean run — prevents stale "0 findings" comments
--format pr-comment-githuboutputs to file, read viapath:— avoids${{ }}injection vectors- No security concern: fallow output is from static analysis, not untrusted PR metadata
Non-blocking note: pull-requests: write is workflow-wide. The inline comment documents why this is acceptable. If fork PRs ever become a concern, moving fallow to its own workflow would scope the permission.
3d59ebb to
7c1098d
Compare
3c5961a to
0c088b7
Compare
|
Thanks @vanceingalls @miguel-heygen — addressed all four points: Fixed
Skipped
Force-pushed @ |
0c088b7 to
521cbd7
Compare
Mirrors the same `fallow audit --base ... --fail-on-issues` check that runs in CI, but locally against HEAD so issues surface at commit time instead of after the push round-trip. Scoped to `packages/**` source files via the glob — non-code edits (README, docs, top-level configs) skip the hook entirely. Measured locally: ~5s in parallel with the existing lint/format/typecheck checks. Doesn't extend wall-clock time because typecheck (~11s) is the long pole, and lefthook runs commands in parallel. The default `--gate new-only` means inherited findings don't block the commit — same gate behavior as CI, so local pre-commit and PR audit agree.
After fallow's auto-fix de-exports unused symbols, oxlint surfaces them as no-unused-vars. This PR deletes those orphan declarations outright. Biggest cleanup: studio/src/icons/SystemIcons.tsx shrinks from 132 to 57 lines — 33 unused icon wrappers and their phosphor-icon imports deleted. Other deletions across 14 more files covering paired getter/setters, helper functions, dead env constants, internal components with no callers, and cascading unused imports. Cascade-causing files held back for follow-up PRs: renderOrchestrator barrel of captureCost re-exports, telemetry/portUtils/remote barrels, Button.tsx + ui/index.ts (would orphan whole file), studioMotion type re-exports. Test plan: typecheck clean across 8 packages, oxlint + oxfmt clean, fallow audit exit 0 (remaining findings inherited), cli + studio vitest suites pass.
Reviewers shouldn't have to dig through CI logs to see what fallow flagged. With this change, on every PR the fallow job posts (or updates) a sticky comment containing the full audit report formatted as a collapsible markdown table. The comment uses fallow's built-in `pr-comment-github` format, which already emits a `<!-- fallow-id: fallow-results -->` sentinel. `marocchino/sticky-pull-request-comment@v2.9.1` matches that header so each run replaces the previous comment instead of stacking new ones. The job now runs in three steps: 1. Run `fallow audit ... --format pr-comment-github` with `continue-on-error: true` so the comment posts even when the audit fails. Exit code is captured. 2. Post (or update) the sticky comment with the captured output. 3. Re-emit the audit exit code so the job still fails-the-build on new findings. Bumps the workflow's `pull-requests` permission from read to write, needed for the sticky-comment poster to call the issues API.
7c1098d to
a67436b
Compare
521cbd7 to
e03d038
Compare
The base branch was changed.
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-reviewed — APPROVE.
All prior feedback addressed in the force-push:
pull-requests: writescoped to job level (not workflow-wide) — addresses the least-privilege concern- Empty markdown guard added (
if [ ! -s /tmp/fallow-comment.md ]) — handles fallow crash edge case continue-on-error: trueon both sticky-comment steps — graceful degradation for fork PRs- PR description updated to match actual behavior
CI all green including fallow audit, full regression suite, typecheck, lint, build.
vanceingalls
left a comment
There was a problem hiding this comment.
Re-review on a67436b4. All three prior findings ADDRESSED — including the important one. Diff is clean: top-level permissions: pull-requests: read, job-level pull-requests: write scoped to fallow: only.
Findings status
- important (workflow-level
pull-requests: write) — ADDRESSED..github/workflows/ci.yml:4-6is nowpermissions: contents: read / pull-requests: read.ci.yml:88-90adds job-levelpermissions: contents: read / pull-requests: writeonfallow:only, with a comment that names exactly the right reason ("the rest ofci.ymlkeeps the workflow-levelpull-requests: readdefault so build / lint / test tokens can't post or modify PR comments"). Build, Lint, Format, Typecheck, Test, smoke jobs, filesize, semantic-pr-title now run with read-only PR scope. Blast-radius pinched as recommended. - nit (empty/partial sticky markdown guard) — ADDRESSED.
ci.yml:113-115:if [ ! -s /tmp/fallow-comment.md ]; then echo "fallow produced no output — see the job logs above." > /tmp/fallow-comment.md; fi. Good fallback message too — points reviewers at the log rather than dropping a blank comment. - nit (fork-PR fail-soft on sticky-comment steps) — ADDRESSED.
ci.yml:121andci.yml:130both havecontinue-on-error: trueon the two sticky-comment uses-blocks. Fork PRs won't fail CI on a comment they can't post; the audit gate (Fail if audit found issuesstep) still runs and gates the build.
New code worth a callout
ci.yml:128-134— "Remove stale sticky comment (clean run)" usingdelete: trueis the right pattern; a PR that goes red→green now sheds its findings comment rather than carrying a stale one indefinitely. (Pre-existing, kept from the prior review SHA; still strong.)
CI status (verbatim, head a67436b4)
All required checks SUCCESS: Build, Lint, Format, Typecheck, Test, Test: runtime contract, CLI smoke (required), Smoke: global install, Fallow audit, File size check, regression-shards (8 shards), preview-regression, player-perf, regression, Semantic PR title, Detect changes, Preflight (lint + format), Graphite / mergeability_check. No FAILURE conclusions. mergeStateStatus: BLOCKED is review-required (this approval clears it), not CI.
baseRefName: main — no stacked-PR CI gap.
Verdict: APPROVE
Reasoning: Important finding addressed exactly as recommended (job-level scoping); both nits cleanly handled. CI fully green on main base. Ship it.
Review by Vai (re-review)

What
Adds a sticky PR comment from the Fallow audit job so reviewers see findings inline instead of digging through CI logs.
How
The Fallow audit job (added in #942) now runs in four steps:
bunx fallow audit ... --fail-on-issues --format pr-comment-github > /tmp/fallow-comment.mdwithset +e. Captures the markdown output and the audit exit code. Includes a non-empty guard so transient fallow crashes don't post a blank sticky.marocchino/sticky-pull-request-comment@v2.9.1, only whenexit_code != '0'. Uses fallow's built-in<!-- fallow-id: fallow-results -->sentinel as theheaderso each subsequent run updates the same comment.delete: true, only whenexit_code == '0'. Clears any previous "findings" sticky after a follow-up commit fixes the issue.The sticky-comment steps both have
continue-on-error: trueso fork PRs (which run with a read-onlyGITHUB_TOKENregardless of workflowpermissions:) don't fail the whole job on a comment they can't post; the audit gate still fires via step 4.Permissions
pull-requests: writeis scoped to thefallowjob only — moved out of the workflow-levelpermissions:block (per Vance's review). Build, lint, test, smoke jobs etc. keep the workflow's read-only default. Job-level permissions override the workflow block, so this scope-down is one extrapermissions:stanza, no other changes needed.What the comment looks like
When there are findings: a collapsible markdown table listing severity, rule, file:line, and description — same content fallow normally writes to stdout.
When there are no findings: no comment is posted, and any stale "findings" sticky from a previous run is deleted.
Test plan
bunx fallow audit --fail-on-issues --format pr-comment-githubemits markdown to stdout AND exits 1 when findings existpermissions:block correctly overrides workflow-level (verified against GitHub Actions docs)Follow-ups
--format review-github) — bigger UX shift